from itertools import permutations
import xlsxwriter, re
from buffer import Buffer


# stage_list_full = [  # 存放所有的stage
#     #(任务标识，操作序号，操作资源，时间，前置任务依赖)
#     ("r1",1,"nfc1",100,(0,)),("r1",2,"nfc1",100,(1,)),("r1",3,"host",20,(2,)),("r1",4,"host",5,(3,)),
#     ("r2",1,"nfc2",100,(0,)),("r2",2,"nfc2",100,(1,)),("r2",3,"host",20,(2,)),("r2",4,"host",5,(3,)),
#     ("w",1,"nfc1",100,(0,)),("w",2,"nfc1",100,(1,)),("w",3,"host",20,(2,)),("w",4,"nfc1",600,(3,)),("w",5,"host",5,(4,)),
#     ("t",1,"host",20,(0,)),("t",2,"nfc1",100,(1,)),("t",3,"nfc1",100,(2,)),("t",4,"nfc1",100,(3,)),("t",5,"nfc2",100,(3,)),("t",6,"nfc1",100,(4,)),("t",7,"nfc2",100,(5,)),("t",8,"matcher1",30,(6,)),("t",9,"matcher2",30,(7,)),("t",10,"host",20,(8,9)),("t",11,"host",5,(10,))
# ]
arm_time = 150
stage_list_full = [
    #(任务标识，操作序号，操作资源，时间，前置任务依赖)
    # ("r1",1,"nfc1",200,(0,),(1,0)), ("r1",2,"host",20,(1,)),
    # ("w1",1,"nfc1",200,(0,),(1,1)), ("w1",2,"host",20,(1,)),
    # ("t",1,"host",20,(0,)),("t",2,"nfc1",200,(1,),(1,2)),("t",3,"nfc1",200,(2,),(1,0)),("t",4,"matcher",150,(3,)),("t",5,"nfc2",200,(2,),(2,0)),("t",6,"matcher",150,(5,)),("t",7,"host",20,(4,6,))
    ["r1",(1,"nfc1",200,(0,),(1,0)),(2,"arm",1,(1,)),(3,"host",20,(2,))],
    ["w1",(1,"nfc1",200,(0,),(1,1)),(2,"arm",1,(1,)),(3,"host",20,(2,))],
    [
        "t",
        (1,"host",20,(0,)),
        (2,"nfc1",200,(1,),(1,2)),
        (3,"arm",1,(2,)),
        (4,"nfc1",200,(3,),(1,0)),
        (5,"arm",1,(4,)),
        (6,"arm",arm_time,(5,)),
        (7,"nfc2",200,(3,),(2,0)),
        (8,"arm",1,(7,)),
        (9,"arm",arm_time,(8,)),
        (10,"host",20,(6,9,))    
        ]
]

def get_stage_list_lite():  # 做格式转换
    stage_list_lite = []
    for task in stage_list_full:
        for stage in task[1:]:  # 遍历处理
            if len(stage) == 5:  # 带了page元组的，末尾加上page
                stage_str = task[0] + "-" + str(stage[0]) + "-" + stage[1] + "-" + str(stage[2]) + "-" + str(stage[3]) + "-" + str(stage[4])
            elif len(stage) == 4:  # 没有带page元组，末尾不加page
                stage_str = task[0] + "-" + str(stage[0]) + "-" + stage[1] + "-" + str(stage[2]) + "-" + str(stage[3]) + "-"
            stage_list_lite.append(stage_str)
    return stage_list_lite


# 考虑利用字典来做依赖检索，提高效率。
# 每次检查依赖都先初始化一个空的检索表
def init_table():
    table = {
        "r1-0": True,
        "w1-0": True,
        "t-0":True
    }
    for task in stage_list_full:
        for stage in task[1:]:
            table[task[0] + "-" + str(stage[0])] = False
    return table


# 检查是否满足依赖关系，满足的话return True
def check_dependency(exe_order:tuple):
    done_table = init_table()  # 先拿一张空的查找表
    for stage in exe_order:  # 开始遍历检查
        dependency_list = tuple(eval(stage.split('-')[4]))  # 先拿当前stage的前置依赖表，注意类型是元组

        for depend_stage_index in dependency_list:  # 遍历，开始查表检查依赖
            if not done_table[stage.split('-')[0] + '-' + str(depend_stage_index)]:  # 当前所依赖stage未完成，此order无效
                return False

        done_table[stage.split('-')[0] + '-' + stage.split('-')[1]] = True  # 在表中标记自己这个stage已经完成

    return True


def compute_legal_order(stage_list_lite:list):  # 计算所有可能的合法顺序
    f = open('legal_order.txt', 'w')

    for order in permutations(stage_list_lite,  len(stage_list_lite)):
        if check_dependency(order):
            order_str = ""
            for stage_str in order:
                order_str = order_str + stage_str + ">>>"
            order_str = order_str[:-3] + '\n'
            print(order_str)
            f.write(order_str)
            
    f.close()


def init_resourses():  # 用来构造初始状态的可用资源表
    resource = {
        'nfc1': ['0-0<>'],
        'nfc2': ['0-0<>'],
        'host': ['0-0<>'],
        'm1': ['0-0<>'],
        'm2': ['0-0<>'],
        'arm': ['0-0<>'],
    }

    return resource

def init_done_table():  # 用来存储已经完成的stage信息，方便查找所依赖的stage的完成时间
    done_table = {
        "r1-0": 0,
        "w1-0": 0,
        "t-0": 0
    }
    return done_table

# todo: 计算时间
def compute_order_time():  # 计算每种执行顺序的时间
    order_file = open('legal_order.txt', 'r')
    all_lines = order_file.readlines()
    order_file.close()
    workbook = xlsxwriter.Workbook('exeTime.xlsx')  # 将每种顺序的执行情况记录到exeTime.xlsx里面
    worksheet = workbook.add_worksheet()  # 创建一个新的sheet
    # 先写第一行的title
    worksheet.write('A1', 'host')
    worksheet.write('B1', 'nfc1')
    worksheet.write('C1', 'nfc2')
    worksheet.write('D1','arm')
    worksheet.write('E1', 'm1')
    worksheet.write('F1', 'm2')
    worksheet.write('G1', 'Time')
    # 构造一个字典，用于得到不同title在表格中所对应的列名
    col_prefix = {'host':'A', 'nfc1':'B', 'nfc2':'C', 'arm':'D', 'm1':'E', 'm2':'F', 'Time':'G'}

    row_index = 1  # 用于确定当前order应该写到excel的哪一行
    for exe_order_str in all_lines:  # 遍历每一种执行顺序
        row_index += 1  # 第0行是标题行，所以要先切到下一行
        # sys_time = 0  # 初始的时间戳
        resources = init_resourses()  # 算每一种order都要重新构造一个新的
        done_table = init_done_table()  # 初始化一个字典，用来存每个stage的完成时间
        buffer_list = [Buffer(1), Buffer(1)]  # 分别对应nfc1和nfc2，默认每个buffer深度为1

        stage_list = exe_order_str[:-1].split('>>>')  # 先用[:-1]去掉末尾的换行符，再split
        for stage in stage_list:  # 开始对stage_list中的每一个stage处理，将其放到对应的位置并计算时间
            words = stage.split('-')  # 先做个切割
            unit_name = words[2]  # 此stage所需的器件。之所以要单独存个变量是因为后面如果是matcher的话unit会发生变化
            # 拿前置依赖stage的完成时间和器件的空闲时间，以两者之间较久的那个为准
            # 先查前置依赖的完成时间，便于分配可用器件
            last_depend_done_time = 0
            depend_stages = tuple(eval(words[4]))
            for stage_index in depend_stages:  # 挨个遍历前置依赖
                if done_table[words[0] + '-' + str(stage_index)] > last_depend_done_time:  # 选择完成时间最晚的那一个存下来
                    last_depend_done_time = done_table[words[0] + '-' + str(stage_index)]

            # 拿器件空闲时间
            unit_free_time = 0
            if words[2] == 'matcher':  # 需要先分配一个确定的matcher
                matcher1_free_time = int(re.match(r'\d+-(\d+)<\w*', resources['m1'][-1]).group(1))  # 这是m1的
                matcher2_free_time = int(re.match(r'\d+-(\d+)<\w*', resources['m2'][-1]).group(1))  # 这是m2的
                if matcher1_free_time <= matcher2_free_time:  # 如果m1的空闲时间<=m2的，那就分配到m1上
                    unit_name = 'm1'
                    unit_free_time = matcher1_free_time
                else:  # 分配到m2上
                    unit_name = 'm2'
                    unit_free_time = matcher2_free_time

            else:  # 不是匹配操作，查resources字典
                last_op = resources[words[2]][-1]  # 拿对应器件的最后执行操作
                unit_free_time = int(re.match(r'\d+-(\d+)<\w*', last_op).group(1))  # 用正则表达式从'0-0<>'中提取出结束时间

            operation_time = int(words[3])
            # 检查buffer命中情况
            if words[5]:  # 如果是需要buffer的
                buffer_info = tuple(eval(words[5]))  # (dieNo, page)
                buffer_instance = buffer_list[buffer_info[0] - 1]  # 先拿到对应channel的buffer instance
                if buffer_instance.is_hit(buffer_info[1]):  # 如果buffer命中了
                    operation_time = 0
                else:  # 未命中，则先分配一个
                    if not buffer_instance.allocate(buffer_info[1]):  # allocate返回了False，说明需要先进行写回
                        operation_time += 600  # 增加一段写回的时间
                if words[0].startswith('w'):  # 如果是一个host写操作，则需要将buffer置脏
                    buffer_instance.mark_dirty(buffer_info[1])
            
            # 接下来就需要计算stage的起始-终止时间了，并保存结果
            start_time = last_depend_done_time if last_depend_done_time > unit_free_time else unit_free_time  # 确定起始时间
            end_time = start_time + operation_time  # 确定结束的时间
            # 记录结果
            resources[unit_name].append(str(start_time) + '-' + str(end_time) + '<' + words[0] + '-' + words[1] + '>')  # 添加到器件时间表中
            done_table[words[0] + '-' + words[1]] = end_time  # 标记此stage的完成时间，便于下次拿前置stage的完成时间

        # 统计此order的最终执行时间
        total_time = sorted(done_table.items(), key = lambda kv:(kv[1], kv[0]))[-1][1]  # 直接对done_table按值排序即可拿到完成时间最晚的那个stage
        worksheet.write(col_prefix['Time'] + str(row_index), total_time)  # 把总时间写道表格中去

        # 将每个器件的执行情况写到表格中去
        for name in resources:  # 遍历resources的键
            if len(resources[name]) > 1:
                content = ', '.join(resources[name][1:])  # 先要拼接成一个字串
                worksheet.write(col_prefix[name] + str(row_index), content)  # 将content写到excel对应的cell中去

        # break

    
    workbook.close()

compute_legal_order(get_stage_list_lite())  # 先算所有的合法排列
compute_order_time()  # 计算执行总时间
